#include "IOFile.h"

int IOFile::WriteFile( LPCTSTR path, LPCTSTR context )
{
#ifdef _UNICODE_CHARACTER
  FILE* file = open_file_wide(path, L"wb");
#else
  FILE* file = fopen(path, "wb");
#endif

  if(!file)
    return -1;

  Writer_File writer(file);
  save(&writer, 0, context);

  fclose(file);

  return 0;
}

LPCTSTR IOFile::ReadFile( LPCTSTR path )
{
  return NULL;
}

void IOFile::save( Writer_File* writer, int encoding, LPCTSTR context)
{
  Buffered_Writer bufferWriter(writer, encoding);
  bufferWriter.write(context);
}

void IOFile::write( LPCTSTR data, size_t length )
{
  
}

#ifdef _UNICODE_CHARACTER

char* IOFile::convert_path_heap( const wchar_t* str )
{
  assert(str);

  // first pass: get length in utf8 characters
  size_t length = wcslen(str);
  size_t size = as_utf8_begin(str, length);

  // allocate resulting string
  char* result = static_cast<char*>(global_allocate(size + 1));
  if (!result) return 0;

  // second pass: convert to utf8
  as_utf8_end(result, size, str, length);

  return result;
}

FILE* IOFile::open_file_wide( const wchar_t* path, const wchar_t* mode )
{
  // there is no standard function to open wide paths, so our best bet is to try utf8 path
  char* path_utf8 = convert_path_heap(path);
  if (!path_utf8) return 0;

  // convert mode to ASCII (we mirror _wfopen interface)
  char mode_ascii[4] = {0};
  for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast<char>(mode[i]);

  // try to open the utf8 path
  FILE* result = fopen(path_utf8, mode_ascii);

  // free dummy buffer
  global_deallocate(path_utf8);

  return result;
}

#endif

Writer_File::Writer_File( void* file )
  : file(file)
{

}

void Writer_File::write( const void* data, size_t size )
{
  fwrite(data, size, 1, static_cast<FILE*>(file));
}

Buffered_Writer::Buffered_Writer( Writer* writer, int encoding )
  : writer(writer), bufsize(0), encoding(encoding)
{

}

Buffered_Writer::~Buffered_Writer()
{
  flush();
}

void Buffered_Writer::flush()
{
  flush(buffer, bufsize);
  bufsize = 0;
}

void Buffered_Writer::flush( LPCTSTR data, size_t size )
{
  if(size == 0) return;

  // fast path, just write data
  if(encoding == 0)
    writer->write(data, size * sizeof(TCHAR));
  else
  {
    //size_t result = convert_buffer()
  }
}

void Buffered_Writer::write( LPCTSTR data )
{
  write(data, _tcslen(data));
}

void Buffered_Writer::write( LPCTSTR data, size_t length )
{
  if (bufsize + length > buffcapacity)
  {
    // flush the remaining buffer contents
    flush();

    // handle large chunks
    if (length > buffcapacity)
    {
      if (encoding == 0)
      {
        // fast path, can just write data chunk
        writer->write(data, length * sizeof(TCHAR));
        return;
      }

      // need to convert in suitable chunks
      while (length > buffcapacity)
      {
        // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer
        // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary)
        //size_t chunk_size = get_valid_length(data, bufcapacity);

        // convert chunk and write
        //flush(data, chunk_size);

        // iterate
        //data += chunk_size;
        //length -= chunk_size;
      }

      // small tail is copied below
      bufsize = 0;
    }
  }

  memcpy(buffer + bufsize, data, length * sizeof(TCHAR));
  bufsize += length;
}
